<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VDO.Ninja SDK - Dynamic Viewer</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }
        .container {
            display: grid;
            grid-template-columns: 350px 1fr;
            gap: 20px;
        }
        .control-panel {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            height: fit-content;
        }
        .video-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
            gap: 10px;
        }
        .video-container {
            background: #000;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
        }
        .video-container video {
            width: 100%;
            height: 240px;
            object-fit: cover;
        }
        .video-label {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 5px 10px;
            font-size: 14px;
        }
        .section {
            margin-bottom: 20px;
            padding-bottom: 20px;
            border-bottom: 1px solid #eee;
        }
        .section:last-child {
            border-bottom: none;
        }
        h2 {
            margin-top: 0;
            font-size: 20px;
            color: #333;
        }
        h3 {
            font-size: 16px;
            margin: 10px 0 5px 0;
            color: #666;
        }
        input, select {
            width: 100%;
            padding: 8px;
            margin: 5px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button {
            background: #007bff;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px 5px 5px 0;
            font-size: 14px;
        }
        button:hover {
            background: #0056b3;
        }
        button:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        button.danger {
            background: #dc3545;
        }
        button.danger:hover {
            background: #c82333;
        }
        button.success {
            background: #28a745;
        }
        button.success:hover {
            background: #218838;
        }
        .status {
            padding: 10px;
            margin: 10px 0;
            background: #f8f9fa;
            border-radius: 4px;
            font-size: 14px;
            border-left: 4px solid #007bff;
        }
        .status.error {
            border-left-color: #dc3545;
            background: #f8d7da;
            color: #721c24;
        }
        .status.success {
            border-left-color: #28a745;
            background: #d4edda;
            color: #155724;
        }
        .participant-list {
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 5px;
            font-size: 14px;
        }
        .participant {
            padding: 5px;
            margin: 2px 0;
            background: #f8f9fa;
            border-radius: 3px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .participant.connected {
            background: #d4edda;
        }
        .tab-buttons {
            display: flex;
            gap: 5px;
            margin-bottom: 15px;
        }
        .tab-button {
            background: #f8f9fa;
            color: #333;
            flex: 1;
        }
        .tab-button.active {
            background: #007bff;
            color: white;
        }
        .tab-content {
            display: none;
        }
        .tab-content.active {
            display: block;
        }
        .stats {
            font-family: monospace;
            font-size: 12px;
            background: #f8f9fa;
            padding: 5px;
            border-radius: 3px;
            margin-top: 5px;
        }
    </style>
</head>
<body>
    <h1>VDO.Ninja SDK - Dynamic Viewer</h1>
    
    <div class="container">
        <!-- Control Panel -->
        <div class="control-panel">
            <div class="section">
                <h2>Connection</h2>
                <input type="text" id="wss" placeholder="WebSocket Server" value="wss://apibackup.vdo.ninja/">
                <button onclick="connect()" id="connectBtn">Connect</button>
                <button onclick="disconnect()" id="disconnectBtn" disabled>Disconnect</button>
                <div id="connectionStatus" class="status">Not connected</div>
            </div>
            
            <!-- Tabs -->
            <div class="tab-buttons">
                <button class="tab-button active" onclick="showTab('room')">Room View</button>
                <button class="tab-button" onclick="showTab('direct')">Direct View</button>
            </div>
            
            <!-- Room View Tab -->
            <div id="roomTab" class="tab-content active">
                <div class="section">
                    <h2>Join Room</h2>
                    <input type="text" id="roomName" placeholder="Room Name">
                    <input type="password" id="roomPassword" placeholder="Password (optional)">
                    <button onclick="joinRoom()" id="joinRoomBtn" disabled>Join Room</button>
                    <button onclick="leaveRoom()" id="leaveRoomBtn" disabled>Leave Room</button>
                    <div id="roomStatus" class="status">Not in a room</div>
                </div>
                
                <div class="section">
                    <h2>Room Participants</h2>
                    <button onclick="refreshParticipants()" id="refreshBtn" disabled>Refresh</button>
                    <button onclick="viewAllInRoom()" id="viewAllBtn" disabled class="success">View All</button>
                    <div id="participantList" class="participant-list">
                        <em>Join a room to see participants</em>
                    </div>
                </div>
            </div>
            
            <!-- Direct View Tab -->
            <div id="directTab" class="tab-content">
                <div class="section">
                    <h2>View Stream Directly</h2>
                    <input type="text" id="directStreamID" placeholder="Stream ID">
                    <input type="password" id="directPassword" placeholder="Password (optional)">
                    <button onclick="viewDirect()" id="viewDirectBtn" disabled>View Stream</button>
                </div>
                
                <div class="section">
                    <h2>Active Viewers</h2>
                    <button onclick="refreshActiveViewers()">Refresh</button>
                    <div id="activeViewersList" class="participant-list">
                        <em>No active viewers</em>
                    </div>
                </div>
            </div>
            
            <div class="section">
                <h2>Quick Actions</h2>
                <button onclick="stopAllViewers()" class="danger">Stop All Viewers</button>
                <button onclick="showStats()">Show Stats</button>
            </div>
        </div>
        
        <!-- Video Grid -->
        <div class="video-grid" id="videoGrid">
            <div style="grid-column: 1/-1; text-align: center; color: #666; padding: 40px;">
                <h3>No streams being viewed</h3>
                <p>Connect and join a room or view streams directly</p>
            </div>
        </div>
    </div>

    <script src="../vdoninja-sdk.js"></script>
    <script>
        let sdk = null;
        const viewers = new Map(); // streamID -> {pc, video, container}
        
        // Initialize
        function init() {
            updateUI();
        }
        
        // Show tab
        function showTab(tab) {
            document.querySelectorAll('.tab-button').forEach(btn => {
                btn.classList.remove('active');
            });
            document.querySelectorAll('.tab-content').forEach(content => {
                content.classList.remove('active');
            });
            
            if (tab === 'room') {
                document.querySelector('.tab-button:nth-child(1)').classList.add('active');
                document.getElementById('roomTab').classList.add('active');
            } else {
                document.querySelector('.tab-button:nth-child(2)').classList.add('active');
                document.getElementById('directTab').classList.add('active');
            }
        }
        
        // Connect to signaling server
        async function connect() {
            const wss = document.getElementById('wss').value;
            const status = document.getElementById('connectionStatus');
            
            try {
                status.textContent = 'Connecting...';
                sdk = new VDONinjaSDK({ 
                    wss,
                    debug: true 
                });
                
                // Setup event listeners
                sdk.addEventListener('connected', () => {
                    status.className = 'status success';
                    status.textContent = 'Connected to signaling server';
                    updateUI();
                });
                
                sdk.addEventListener('disconnected', () => {
                    status.className = 'status error';
                    status.textContent = 'Disconnected from server';
                    updateUI();
                });
                
                sdk.addEventListener('someoneJoined', (e) => {
                    console.log('Someone joined:', e.detail);
                    setTimeout(refreshParticipants, 500);
                });
                
                sdk.addEventListener('peerListing', (e) => {
                    console.log('Peer listing:', e.detail);
                    refreshParticipants();
                });
                
                await sdk.connect();
                
            } catch (error) {
                status.className = 'status error';
                status.textContent = `Connection failed: ${error.message}`;
                sdk = null;
                updateUI();
            }
        }
        
        // Disconnect
        async function disconnect() {
            if (sdk) {
                await stopAllViewers();
                await sdk.disconnect();
                sdk = null;
                updateUI();
            }
        }
        
        // Join room
        async function joinRoom() {
            const room = document.getElementById('roomName').value;
            const password = document.getElementById('roomPassword').value;
            const status = document.getElementById('roomStatus');
            
            if (!room) {
                status.className = 'status error';
                status.textContent = 'Please enter a room name';
                return;
            }
            
            try {
                status.textContent = 'Joining room...';
                await sdk.joinRoom({ room, password });
                
                status.className = 'status success';
                status.textContent = `Joined room: ${room}`;
                updateUI();
                
                // Refresh participants after a short delay
                setTimeout(refreshParticipants, 1000);
                
            } catch (error) {
                status.className = 'status error';
                status.textContent = `Failed to join room: ${error.message}`;
            }
        }
        
        // Leave room
        async function leaveRoom() {
            const status = document.getElementById('roomStatus');
            
            try {
                await stopAllViewers();
                await sdk.leaveRoom();
                
                status.className = 'status';
                status.textContent = 'Left room';
                document.getElementById('participantList').innerHTML = '<em>Join a room to see participants</em>';
                updateUI();
                
            } catch (error) {
                status.className = 'status error';
                status.textContent = `Error leaving room: ${error.message}`;
            }
        }
        
        // Refresh participants
        function refreshParticipants() {
            if (!sdk || !sdk.state.room) return;
            
            const participants = sdk.getRoomParticipants();
            const list = document.getElementById('participantList');
            
            if (participants.length === 0) {
                list.innerHTML = '<em>No other participants in room</em>';
                return;
            }
            
            list.innerHTML = participants.map(p => {
                const isViewing = viewers.has(p.streamID);
                const label = p.label || p.streamID;
                return `
                    <div class="participant ${p.connected ? 'connected' : ''}">
                        <span>${label}</span>
                        ${isViewing ? 
                            `<button onclick="stopViewing('${p.streamID}')">Stop</button>` :
                            `<button onclick="viewStream('${p.streamID}')">View</button>`
                        }
                    </div>
                `;
            }).join('');
        }
        
        // View all in room
        async function viewAllInRoom() {
            try {
                const viewers = await sdk.viewAllInRoom();
                
                for (const viewer of viewers) {
                    handleNewViewer(viewer.streamID, viewer.pc, viewer.label);
                }
                
                if (viewers.length === 0) {
                    alert('No streams to view in this room');
                }
                
            } catch (error) {
                console.error('Error viewing all:', error);
                alert(`Error: ${error.message}`);
            }
        }
        
        // View specific stream
        async function viewStream(streamID) {
            try {
                const pc = await sdk.view(streamID);
                handleNewViewer(streamID, pc);
                refreshParticipants();
                refreshActiveViewers();
            } catch (error) {
                console.error(`Error viewing ${streamID}:`, error);
                alert(`Failed to view stream: ${error.message}`);
            }
        }
        
        // View direct (without room)
        async function viewDirect() {
            const streamID = document.getElementById('directStreamID').value;
            const password = document.getElementById('directPassword').value;
            
            if (!streamID) {
                alert('Please enter a stream ID');
                return;
            }
            
            try {
                // If password provided, we need to set it temporarily
                if (password) {
                    sdk.state.password = password;
                    await sdk._updateStreamIDHash();
                }
                
                const pc = await sdk.view(streamID);
                handleNewViewer(streamID, pc);
                refreshActiveViewers();
                
            } catch (error) {
                console.error('Error viewing directly:', error);
                alert(`Failed to view stream: ${error.message}`);
            }
        }
        
        // Handle new viewer connection
        function handleNewViewer(streamID, pc, label) {
            if (viewers.has(streamID)) {
                console.log('Already viewing:', streamID);
                return;
            }
            
            // Create video container
            const container = document.createElement('div');
            container.className = 'video-container';
            container.id = `video-${streamID}`;
            
            const video = document.createElement('video');
            video.autoplay = true;
            video.playsinline = true;
            
            const labelDiv = document.createElement('div');
            labelDiv.className = 'video-label';
            labelDiv.innerHTML = `
                ${label || streamID}
                <button style="float: right; background: #dc3545; border: none; color: white; padding: 2px 8px; border-radius: 3px; cursor: pointer;" 
                        onclick="stopViewing('${streamID}')">×</button>
            `;
            
            container.appendChild(video);
            container.appendChild(labelDiv);
            
            // Handle tracks
            pc.ontrack = (event) => {
                console.log(`Track received from ${streamID}:`, event.track.kind);
                video.srcObject = event.streams[0];
            };
            
            // Handle connection state
            pc.onconnectionstatechange = () => {
                console.log(`Connection state for ${streamID}:`, pc.connectionState);
                if (pc.connectionState === 'failed' || pc.connectionState === 'closed') {
                    stopViewing(streamID);
                }
            };
            
            // Store viewer info
            viewers.set(streamID, { pc, video, container });
            
            // Update grid
            updateVideoGrid();
        }
        
        // Stop viewing specific stream
        async function stopViewing(streamID) {
            const viewer = viewers.get(streamID);
            if (!viewer) return;
            
            // Stop video
            if (viewer.video.srcObject) {
                viewer.video.srcObject.getTracks().forEach(track => track.stop());
                viewer.video.srcObject = null;
            }
            
            // Remove from DOM
            viewer.container.remove();
            
            // Close connection
            await sdk.stopViewing(streamID);
            
            // Remove from map
            viewers.delete(streamID);
            
            // Update UI
            updateVideoGrid();
            refreshParticipants();
            refreshActiveViewers();
        }
        
        // Stop all viewers
        async function stopAllViewers() {
            const streamIDs = Array.from(viewers.keys());
            for (const streamID of streamIDs) {
                await stopViewing(streamID);
            }
        }
        
        // Refresh active viewers list
        function refreshActiveViewers() {
            if (!sdk) return;
            
            const active = sdk.getActiveViewers();
            const list = document.getElementById('activeViewersList');
            
            if (active.length === 0) {
                list.innerHTML = '<em>No active viewers</em>';
                return;
            }
            
            list.innerHTML = active.map(v => `
                <div class="participant ${v.connectionState === 'connected' ? 'connected' : ''}">
                    <span>${v.streamID} (${v.connectionState})</span>
                    <button onclick="stopViewing('${v.streamID}')">Stop</button>
                </div>
            `).join('');
        }
        
        // Update video grid
        function updateVideoGrid() {
            const grid = document.getElementById('videoGrid');
            
            if (viewers.size === 0) {
                grid.innerHTML = `
                    <div style="grid-column: 1/-1; text-align: center; color: #666; padding: 40px;">
                        <h3>No streams being viewed</h3>
                        <p>Connect and join a room or view streams directly</p>
                    </div>
                `;
            } else {
                grid.innerHTML = '';
                for (const viewer of viewers.values()) {
                    grid.appendChild(viewer.container);
                }
            }
        }
        
        // Show stats
        async function showStats() {
            if (!sdk) {
                alert('Not connected');
                return;
            }
            
            try {
                const stats = await sdk.getStats();
                console.log('Connection stats:', stats);
                
                let message = 'Connection Statistics:\n\n';
                for (const [uuid, stat] of Object.entries(stats)) {
                    message += `Connection ${uuid}:\n`;
                    stat.forEach(report => {
                        if (report.type === 'inbound-rtp' || report.type === 'outbound-rtp') {
                            message += `  ${report.type}: ${report.bytesReceived || report.bytesSent || 0} bytes\n`;
                        }
                    });
                }
                
                alert(message);
            } catch (error) {
                alert(`Error getting stats: ${error.message}`);
            }
        }
        
        // Update UI state
        function updateUI() {
            const connected = sdk && sdk.state.connected;
            const inRoom = connected && sdk.state.room;
            
            document.getElementById('connectBtn').disabled = connected;
            document.getElementById('disconnectBtn').disabled = !connected;
            document.getElementById('joinRoomBtn').disabled = !connected || inRoom;
            document.getElementById('leaveRoomBtn').disabled = !inRoom;
            document.getElementById('refreshBtn').disabled = !inRoom;
            document.getElementById('viewAllBtn').disabled = !inRoom;
            document.getElementById('viewDirectBtn').disabled = !connected;
        }
        
        // Initialize on load
        init();
    </script>
</body>
</html>